package sexcel

import (
	"errors"
	"fmt"
	"github.com/xuri/excelize/v2"
	"io"
	"reflect"
	"regexp"
	"strings"
)

// "github.com/xuri/excelize/v2" 文档网站地址：https://xuri.me/excelize/zh-hans/cell.html
const (
	External   = "External"
	Location   = "Location"
	StructXlsx = "xlsx"
)

var (
	HeaderR int = 1
	HeaderC int = 1
	DataR   int = 2
	DataC   int = 1
)

type Sexcel struct {
	*excelize.File
	SheetName string
}

func NewSexcel() *Sexcel {

	se := &Sexcel{
		excelize.NewFile(),
		"Sheet1",
	}

	return se
}

func NewSexcelSheet(sheet string) *Sexcel {

	se := &Sexcel{
		excelize.NewFile(),
		sheet,
	}

	defaultSheetName := se.GetSheetName(0)
	se.SetSheetName(defaultSheetName, se.SheetName)

	return se
}

// 创建新的工作表
func (se *Sexcel) NewSeSheet(sheet string) (int, error) {
	se.SheetName = sheet
	return se.NewSheet(sheet)
}

// 设置将单元格转换成字符串
func (se *Sexcel) SetSeText(cell, value string) error {
	se.SetCellFormula(se.SheetName, cell, value)
	return se.SetCellStr(se.SheetName, cell, value)
}

// 设置将单元格转换成字段
func (se *Sexcel) SetSeValue(cell string, value interface{}) error {
	v := reflect.ValueOf(value)

	switch v.Kind() {
	case reflect.String:
		return se.SetCellStr(se.SheetName, cell, value.(string))
	case reflect.Int:
		return se.SetCellInt(se.SheetName, cell, value.(int))
	case reflect.Int8:
		return se.SetCellInt(se.SheetName, cell, int(value.(int8)))
	case reflect.Int16:
		return se.SetCellInt(se.SheetName, cell, int(value.(int16)))
	case reflect.Int32:
		return se.SetCellInt(se.SheetName, cell, int(value.(int32)))
	case reflect.Int64:
		return se.SetCellValue(se.SheetName, cell, int(value.(int64)))
	case reflect.Bool:
		return se.SetCellBool(se.SheetName, cell, value.(bool))
	case reflect.Float32:
		return se.SetCellFloat(se.SheetName, cell, value.(float64), 8, 32)
	case reflect.Float64:
		return se.SetCellFloat(se.SheetName, cell, value.(float64), 8, 64)
	default:
		return se.SetCellDefault(se.SheetName, cell, value.(string))
	}

}

// 设置单元格样式
func (se *Sexcel) SetSeCellStyle(top string, buttom string, inputStyle *excelize.Style) error {
	style, err := se.NewStyle(inputStyle)
	if err != nil {
		fmt.Println(err)
	}
	//err = se.SetCellStyle(se.SheetName, "D7", "D7", style)
	err = se.SetCellStyle(se.SheetName, top, buttom, style)
	if err != nil {
		return err
	} else {
		return nil
	}
}

// 设置超链接
func (se *Sexcel) SetSeCellHyperLink(cell string, link string, linktype string, tooltip string) {
	display, tooltip := link, tooltip
	if err := se.SetCellHyperLink(se.SheetName, cell,
		link, linktype, excelize.HyperlinkOpts{
			Display: &display,
			Tooltip: &tooltip,
		}); err != nil {
		fmt.Println(err)
	}
	// 为单元格设置字体和下划线样式
	style, err := se.NewStyle(&excelize.Style{
		Font: &excelize.Font{Color: "1265BE", Underline: "single"},
	})
	if err != nil {
		fmt.Println(err)
	}
	err = se.SetCellStyle(se.SheetName, cell, cell, style)
}

// 解析集合创建excel
func (se *Sexcel) ListToExcel(data interface{}) error {

	t, bo := getUnderlyingStructType(data, reflect.Struct)
	if !bo {
		return errors.New("无法解析此集合")
	}

	switch t.Kind() {
	case reflect.Struct:
		// 创建header
		var headerrow = HeaderR
		var headecell = HeaderC
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			tag := field.Tag.Get(StructXlsx)
			cell, _ := excelize.CoordinatesToCellName(headecell, headerrow)

			err := se.SetCellValue(se.SheetName, cell, tag)
			if err != nil {
				panic(err)
			}

			headecell++
		}

		// 遍历内容输出
		var datarow = DataR
		var datacell = DataC

		dv := reflect.ValueOf(data)

		for i := 1; i < dv.Len(); i++ {
			elem := dv.Index(i)

			if elem.Kind() == reflect.Ptr {
				elem = elem.Elem()
			}
			vt := elem.Type()

			for j := 0; j < vt.NumField(); j++ {
				cell, _ := excelize.CoordinatesToCellName(datacell+j, datarow+i-1)

				fieldValue := elem.Field(j).Interface()

				err := se.SetCellValue(se.SheetName, cell, fieldValue)
				if err != nil {
					panic(err)
				}
			}
		}
	}

	return nil
}

// 批量处理单元格合并
func (se *Sexcel) MergeCells(cells ...interface{}) error {

	var style = &excelize.Style{
		Alignment: &excelize.Alignment{
			Horizontal: "center", // 水平居中
			Vertical:   "center", // 垂直居中
		}}

	for index, arg := range cells {
		switch v := arg.(type) {
		case MergeCellType:
			bo := SeMustCompile(v.FieldNameS)
			if !bo {
				return errors.New("参数FieldNameS字段传入参数不符合要求")
			}
			bo = SeMustCompile(v.FieldNameE)
			if !bo {
				return errors.New("参数FieldNameE字段传入参数不符合要求")
			}

			err := se.MergeCell(se.SheetName, v.FieldNameS, v.FieldNameE)
			if err != nil {
				return err
			}
			err = se.SetSeCellStyle(v.FieldNameS, v.FieldNameE, style)
			if err != nil {
				return err
			}
		case []MergeCellType:
			for _, d := range v {
				bo := SeMustCompile(d.FieldNameS)
				if !bo {
					return errors.New("参数FieldNameS字段传入参数不符合要求")
				}
				bo = SeMustCompile(d.FieldNameE)
				if !bo {
					return errors.New("参数FieldNameE字段传入参数不符合要求")
				}

				err := se.MergeCell(se.SheetName, d.FieldNameS, d.FieldNameE)
				if err != nil {
					return err
				}
				err = se.SetSeCellStyle(d.FieldNameS, d.FieldNameE, style)
				if err != nil {
					return err
				}
			}
		case string:
			l := strings.Split(v, ":")
			//判断是否符合
			if len(l) == 2 {
				bo := SeMustCompile(l[0])

				if !bo {
					return errors.New(fmt.Sprintf("第%s条的%s传参格式不对,请输入如 A1,A2这种的格式", index, l[0]))
				}
				bo = SeMustCompile(l[1])
				if !bo {
					return errors.New(fmt.Sprintf("第%s条的%s传参格式不对,请输入如 A1,A2这种的格式", index, l[1]))
				}
				err := se.MergeCell(se.SheetName, l[0], l[1])
				if err != nil {
					return err
				}
				err = se.SetSeCellStyle(l[0], l[1], style)
				if err != nil {
					return err
				}
			} else {
				return errors.New("请输入长度为2的字符串")
			}
		default:
			return errors.New("传入的格式不符合要求")
		}
	}

	return nil
}

// 获取导入的文件
// Load 架子
func (se *Sexcel) NewLoad(data io.Reader, res interface{}, headRow ...int) error {
	if !isPtr(res) {
		return ResNotValid
	}
	f, err := excelize.OpenReader(data)
	if err != nil {
		return err
	}
	f.GetActiveSheetIndex()
	result, err := f.GetRows(f.GetSheetName(f.GetActiveSheetIndex()))
	if err != nil {
		return err
	}
	headIndex := 1
	if len(headRow) != 0 {
		headIndex = headRow[0]
	}

	dataListIndex, err := reflectStructList(res, result[headIndex])
	if err != nil {
		return err
	}
	out := make([]interface{}, 0)
	dataIndex := headIndex + 1
	typeObj := reflectListType(res)
	for i := dataIndex; i < len(result); i++ {
		obj := reflect.New(typeObj)
		for _, v := range dataListIndex {
			if len(result[i])-1 < v.Index {
				continue
			}
			v.setValue(obj.Elem().FieldByName(v.Name), result[i][v.Index])
		}
		out = append(out, obj)

	}
	err = gconv.Struct(out, res)
	if err != nil {
		return err
	}
	return nil
}

// getUnderlyingStructType 获取 data 的底层类型，直到是 reflect.Struct
func getUnderlyingStructType(data interface{}, kind reflect.Kind) (reflect.Type, bool) {
	// 获取 data 的反射类型
	t := reflect.TypeOf(data)

	// 循环解引用指针或接口
	for t != nil {
		// 如果当前类型是结构体，返回
		if t.Kind() == kind {
			return t, true
		}

		// 如果当前类型是指针或接口，解引用
		if t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface || t.Kind() == reflect.Slice {
			t = t.Elem()
			continue
		}

		// 如果不是指针或接口，且不是结构体，返回 nil
		break
	}

	// 如果没有找到结构体类型，返回 nil 和 false
	return nil, false
}

// 校验输入的cell 是否符合格式 第一个英文字母，第二个数字
func SeMustCompile(cell string) bool {
	// 定义正则表达式
	Regex := `^[A-Z][0-9]$` // 匹配一个或多个英文字母

	// 编译正则表达式
	Pattern := regexp.MustCompile(Regex)

	// 验证第一个字符串是否为英文字母
	if Pattern.MatchString(cell) {
		return true
	} else {
		return false
	}

}
